<?php

/**
 * Class DB
 * 数据库操作类
 */

/**
 * Class DB
 * 数据库操作类
 */
class pdodb {

    /**
     * @var
     * @return CDB
     */
    private static $db;

    /*     * 获取CDb类
     * @param $table_name 表名称
     * @param string $db_setting 调用数据库配置项
     * @param array $db_config 数据库配置
     * @return CDb
     */

    public static function cdb() {
        if (!isset(self::$db)) {
            $db = new pdo_db();
            self::$db = $db;
        } else {
            $db = self::$db;
        }
        return $db;
    }


    /**
     * 执行删除记录操作
     * @param $table 表名称
     * @param $condition 	删除数据条件,不充许为空。可以为数组
     * @return boolean
     */
    public static function delete($table, $params = array(), $glue = 'AND') {
        $db = self::cdb();
        return $db->delete($table, $params, $glue);
    }

    /**
     * 执行添加记录操作
     * @param $table 表名称
     * @param array $data 		要增加的数据，参数为数组。数组key为字段值，数组值为数据取值
     * @param bool $return_insert_id 是否返回新建ID号
     * @param bool $replace 是否采用 replace into的方式添加数据
     * @return boolean
     */
    public static function insert($table, $data = array(), $replace = FALSE) {
        $db = self::cdb();
        return $db->insert($table, $data, $replace);
    }

    /**
     * 获取最后一次添加记录的主键号
     * @return int
     */
    public static function insertID() {
        $db = self::cdb();
        return $db->insertid();
    }


    public static function update($table, $data = array(), $params = array(), $glue = 'AND') {
        $db = self::cdb();
        return $db->update($table, $data,$params,$glue);
    }
    
    //得到一条记录
    public static function getOne($tablename, $params = array(), $fields = array()) {
        $db = self::cdb();
        return $db->get($tablename, $params, $fields);
    }
    
    public static function fetch($sql, $params = array()) {
        $db = self::cdb();
        return $db->fetch($sql, $params);
    }

    /**
     * 执行sql查询
     * @param $sql 		查询条件
     * @return array	查询结果集数组
     */
    public static function fetchAll($sql, $params = array(), $keyfield = '') {
        $db = self::cdb();
        return $db->fetchAll($sql, $params, $keyfield);
    }

    /**
     * 直接执行sql查询
     * @param $sql							查询sql语句
     * @return
     */
    public static function query($sql, $params = array()) {
        $db = self::cdb();
        return $db->query($sql,$params);
    }

    public static function startTrans() {
        $db = self::cdb();
        return $db->begin();
    }
    
    public static function commit() {
        $db = self::cdb();
        return $db->commit();
    }

    public static function rollback() {
        $db = self::cdb();
        return $db->rollback();
    }

    public function tableexists($table) {
        $db = self::cdb();
        return $db->tableexists($table);
    }

}



class pdo_db {

    protected $pdo;
    protected $cfg;
    protected $tablepre;
    protected $result;
    protected $statement;
    protected $errors = array();
    protected $link = array();
    protected $pdo_debug = false;

    public function getPDO() {

        return $this->pdo;
    }

    public function __construct($name = 'master') {
        global $dbhost, $dbuser, $dbpassword, $dbname, $dbport, $debug;
        $con['master']['host'] = $dbhost;
        $con['master']['username'] = $dbuser;
        $con['master']['password'] = $dbpassword;
        $con['master']['port'] = $dbport;
        $con['master']['database'] = $dbname;
        $con['master']['charset'] = 'utf8';
        $con['master']['pconnect'] = 0;
        $con['master']['tablepre'] = '';
        $this->pdo_debug = $debug ? true : false;
        $this->cfg = $con;
        $this->connect($name);
    }

    public function connect($name = 'master') {

        if (is_array($name)) {

            $cfg = $name;
        } else {

            $cfg = $this->cfg[$name];
        }

        $this->tablepre = $cfg['tablepre'];

        if (empty($cfg)) {

            exit("The master database is not found, Please checking 'data/config.php'");
        }

        $dsn = "mysql:dbname={$cfg['database']};host={$cfg['host']};port={$cfg['port']}";

        $dbclass = '';

        $options = array();

        if (class_exists('PDO')) {

            if (extension_loaded("pdo_mysql") && in_array('mysql', PDO::getAvailableDrivers())) {

                $dbclass = 'PDO';

                $options = array(PDO::ATTR_PERSISTENT => $cfg['pconnect']);
            } else {

                if (!class_exists('_PDO')) {

                    include ROOT . '/core/pdo/PDO.class.php';
                }

                $dbclass = '_PDO';
            }
        } else {

            include ROOT . '/core/pdo/PDO.class.php';

            $dbclass = 'PDO';
        }

        $this->pdo = new $dbclass($dsn, $cfg['username'], $cfg['password'], $options);

        $sql = "SET NAMES '{$cfg['charset']}';";

        $this->pdo->exec($sql);

        $this->pdo->exec("SET sql_mode='';");

        if (is_string($name)) {

            $this->link[$name] = $this->pdo;
        }

        if ($this->pdo_debug) {

            $info = array();

            $info['sql'] = $sql;

            $info['error'] = $this->pdo->errorInfo();

            $this->debug(false, $info);
        }
    }

    public function prepare($sql) {

        $statement = $this->pdo->prepare($sql);

        return $statement;
    }

    public function query($sql, $params = array()) {

        $starttime = microtime();

        if (empty($params)) {

            $result = $this->pdo->exec($sql);

            if ($this->pdo_debug) {

                $info = array();

                $info['sql'] = $sql;

                $info['error'] = $this->pdo->errorInfo();

                $this->debug(false, $info);
            }

            return $result;
        }

        $statement = $this->prepare($sql);

        $result = $statement->execute($params);

        if ($this->pdo_debug) {

            $info = array();

            $info['sql'] = $sql;

            $info['params'] = $params;

            $info['error'] = $statement->errorInfo();

            $this->debug(false, $info);
        }

        $endtime = microtime();

        $this->performance($sql, $endtime - $starttime);

        if (!$result) {

            return false;
        } else {

            return $statement->rowCount();
        }
    }

    public function fetchcolumn($sql, $params = array(), $column = 0) {

        $starttime = microtime();

        $statement = $this->prepare($sql);

        $result = $statement->execute($params);

        if ($this->pdo_debug) {

            $info = array();

            $info['sql'] = $sql;

            $info['params'] = $params;

            $info['error'] = $statement->errorInfo();

            $this->debug(false, $info);
        }

        $endtime = microtime();

        $this->performance($sql, $endtime - $starttime);

        if (!$result) {

            return false;
        } else {

            return $statement->fetchColumn($column);
        }
    }

    public function fetch($sql, $params = array()) {

        $starttime = microtime();

        $statement = $this->prepare($sql);

        $result = $statement->execute($params);

        if ($this->pdo_debug) {

            $info = array();

            $info['sql'] = $sql;

            $info['params'] = $params;

            $info['error'] = $statement->errorInfo();

            $this->debug(false, $info);
        }

        $endtime = microtime();

        $this->performance($sql, $endtime - $starttime);

        if (!$result) {

            return false;
        } else {

            return $statement->fetch(pdo::FETCH_ASSOC);
        }
    }

    public function fetchall($sql, $params = array(), $keyfield = '') {

        $starttime = microtime();

        $statement = $this->prepare($sql);

        $result = $statement->execute($params);

        if ($this->pdo_debug) {

            $info = array();

            $info['sql'] = $sql;

            $info['params'] = $params;

            $info['error'] = $statement->errorInfo();

            $this->debug(false, $info);
        }

        $endtime = microtime();

        $this->performance($sql, $endtime - $starttime);

        if (!$result) {

            return false;
        } else {

            if (empty($keyfield)) {

                return $statement->fetchAll(pdo::FETCH_ASSOC);
            } else {

                $temp = $statement->fetchAll(pdo::FETCH_ASSOC);

                $rs = array();

                if (!empty($temp)) {

                    foreach ($temp as $key => &$row) {

                        if (isset($row[$keyfield])) {

                            $rs[$row[$keyfield]] = $row;
                        } else {

                            $rs[] = $row;
                        }
                    }
                }

                return $rs;
            }
        }
    }

    public function get($tablename, $params = array(), $fields = array()) {

        $select = '*';

        if (!empty($fields)) {

            if (is_array($fields)) {

                $select = '`' . implode('`,`', $fields) . '`';
            } else {

                $select = $fields;
            }
        }

        $condition = $this->implode($params, 'AND');

        $sql = "SELECT {$select} FROM " . $this->tablename($tablename) . (!empty($condition['fields']) ? " WHERE {$condition['fields']}" : '') . " LIMIT 1";

        return $this->fetch($sql, $condition['params']);
    }

    public function getall($tablename, $params = array(), $fields = array(), $keyfield = '') {

        $select = '*';

        if (!empty($fields)) {

            if (is_array($fields)) {

                $select = '`' . implode('`,`', $fields) . '`';
            } else {

                $select = $fields;
            }
        }

        $condition = $this->implode($params, 'AND');

        $sql = "SELECT {$select} FROM " . $this->tablename($tablename) . (!empty($condition['fields']) ? " WHERE {$condition['fields']}" : '') . $limitsql;

        return $this->fetchall($sql, $condition['params'], $keyfield);
    }

    public function getslice($tablename, $params = array(), $limit = array(), &$total = null, $fields = array(), $keyfield = '') {

        $select = '*';

        if (!empty($fields)) {

            if (is_array($fields)) {

                $select = '`' . implode('`,`', $fields) . '`';
            } else {

                $select = $fields;
            }
        }

        $condition = $this->implode($params, 'AND');

        if (!empty($limit)) {

            if (is_array($limit)) {

                $limitsql = " LIMIT " . ($limit[0] - 1) * $limit[1] . ', ' . $limit[1];
            } else {

                $limitsql = strexists(strtoupper($limit), 'LIMIT') ? " $limit " : " LIMIT $limit";
            }
        }

        $sql = "SELECT {$select} FROM " . $this->tablename($tablename) . (!empty($condition['fields']) ? " WHERE {$condition['fields']}" : '') . $limitsql;

        $total = pdo_fetchcolumn("SELECT COUNT(*) FROM " . tablename($tablename) . (!empty($condition['fields']) ? " WHERE {$condition['fields']}" : ''), $condition['params']);

        return $this->fetchall($sql, $condition['params'], $keyfield);
    }

    public function getcolumn($tablename, $params = array(), $field) {

        $result = $this->get($tablename, $params, array($field));

        if (!empty($result)) {

            return $result[$field];
        } else {

            return false;
        }
    }

    public function update($table, $data = array(), $params = array(), $glue = 'AND') {

        $fields = $this->implode($data, ',');

        $condition = $this->implode($params, $glue);

        $params = array_merge($fields['params'], $condition['params']);

        $sql = "UPDATE " . $this->tablename($table) . " SET {$fields['fields']}";

        $sql .= $condition['fields'] ? ' WHERE ' . $condition['fields'] : '';

        return $this->query($sql, $params);
    }

    public function insert($table, $data = array(), $replace = FALSE) {

        $cmd = $replace ? 'REPLACE INTO' : 'INSERT INTO';

        $condition = $this->implode($data, ',');

        return $this->query("$cmd " . $this->tablename($table) . " SET {$condition['fields']}", $condition['params']);
    }

    public function insertid() {

        return $this->pdo->lastInsertId();
    }

    public function delete($table, $params = array(), $glue = 'AND') {

        $condition = $this->implode($params, $glue);

        $sql = "DELETE FROM " . $this->tablename($table);

        $sql .= $condition['fields'] ? ' WHERE ' . $condition['fields'] : '';

        return $this->query($sql, $condition['params']);
    }

    public function begin() {

        $this->pdo->beginTransaction();
    }

    public function commit() {

        $this->pdo->commit();
    }

    public function rollback() {
        $this->pdo->rollBack();
    }

    private function implode($params, $glue = ',') {

        $result = array('fields' => ' 1 ', 'params' => array());

        $split = '';

        $suffix = '';

        $allow_operator = array('>', '<', '<>', '!=', '>=', '<=', '+=', '-=', 'LIKE', 'like');

        if (in_array(strtolower($glue), array('and', 'or'))) {

            $suffix = '__';
        }

        if (!is_array($params)) {

            $result['fields'] = $params;

            return $result;
        }

        if (is_array($params)) {

            $result['fields'] = '';

            foreach ($params as $fields => $value) {

                $operator = '';

                if (strpos($fields, ' ') !== FALSE) {

                    list($fields, $operator) = explode(' ', $fields, 2);

                    if (!in_array($operator, $allow_operator)) {

                        $operator = '';
                    }
                }

                if (empty($operator)) {

                    $fields = trim($fields);

                    if (is_array($value)) {

                        $operator = 'IN';
                    } else {

                        $operator = '=';
                    }
                } elseif ($operator == '+=') {

                    $operator = " = `$fields` + ";
                } elseif ($operator == '-=') {

                    $operator = " = `$fields` - ";
                }

                if (is_array($value)) {

                    $insql = array();

                    foreach ($value as $k => $v) {

                        $insql[] = ":{$suffix}{$fields}_{$k}";

                        $result['params'][":{$suffix}{$fields}_{$k}"] = is_null($v) ? '' : $v;
                    }

                    $result['fields'] .= $split . "`$fields` {$operator} (" . implode(",", $insql) . ")";

                    $split = ' ' . $glue . ' ';
                } else {

                    $result['fields'] .= $split . "`$fields` {$operator}  :{$suffix}$fields";

                    $split = ' ' . $glue . ' ';

                    $result['params'][":{$suffix}$fields"] = is_null($value) ? '' : $value;
                }
            }
        }

        return $result;
    }

    public function run($sql, $stuff = 'ims_') {

        if (!isset($sql) || empty($sql))
            return;



        $sql = str_replace("\r", "\n", str_replace(' ' . $stuff, ' ' . $this->tablepre, $sql));

        $sql = str_replace("\r", "\n", str_replace(' `' . $stuff, ' `' . $this->tablepre, $sql));

        $ret = array();

        $num = 0;

        $sql = preg_replace("/\;[ \f\t\v]+/", ';', $sql);

        foreach (explode(";\n", trim($sql)) as $query) {

            $ret[$num] = '';

            $queries = explode("\n", trim($query));

            foreach ($queries as $query) {

                $ret[$num] .= (isset($query[0]) && $query[0] == '#') || (isset($query[1]) && isset($query[1]) && $query[0] . $query[1] == '--') ? '' : $query;
            }

            $num++;
        }

        unset($sql);

        foreach ($ret as $query) {

            $query = trim($query);

            if ($query) {

                $this->query($query, array());
            }
        }
    }

    public function fieldexists($tablename, $fieldname) {

        $isexists = $this->fetch("DESCRIBE " . $this->tablename($tablename) . " `{$fieldname}`", array());

        return !empty($isexists) ? true : false;
    }

    public function indexexists($tablename, $indexname) {

        if (!empty($indexname)) {

            $indexs = $this->fetchall("SHOW INDEX FROM " . $this->tablename($tablename), array(), '');

            if (!empty($indexs) && is_array($indexs)) {

                foreach ($indexs as $row) {

                    if ($row['Key_name'] == $indexname) {

                        return true;
                    }
                }
            }
        }

        return false;
    }

    public function tablename($table) {

        return "`{$this->tablepre}{$table}`";
    }

    public function debug($output = true, $append = array()) {

        if (!empty($append)) {

            $output = false;

            array_push($this->errors, $append);
        }

        if ($output) {

            print_r($this->errors);
        } else {

            if (!empty($append['error'][1])) {

                $traces = debug_backtrace();

                $ts = '';

                foreach ($traces as $trace) {

                    $trace['file'] = str_replace('\\', '/', $trace['file']);

                    $trace['file'] = str_replace(IA_ROOT, '', $trace['file']);

                    $ts .= "file: {$trace['file']}; line: {$trace['line']}; <br />";
                }

                $params = var_export($append['params'], true);

                if (!function_exists('message')) {

                    load()->web('common');

                    load()->web('template');
                }

                message("SQL: <br/>{$append['sql']}<hr/>Params: <br/>{$params}<hr/>SQL Error: <br/>{$append['error'][2]}<hr/>Traces: <br/>{$ts}");
            }
        }

        return $this->errors;
    }

    public function tableexists($table) {

        if (!empty($table)) {

            $data = $this->fetch("SHOW TABLES LIKE '{$this->tablepre}{$table}'", array());

            if (!empty($data)) {

                $data = array_values($data);

                $tablename = $this->tablepre . $table;

                if (in_array($tablename, $data)) {

                    return true;
                } else {

                    return false;
                }
            } else {

                return false;
            }
        } else {

            return false;
        }
    }
    
    private function performance($sql, $runtime = 0) {
		if ($runtime == 0) {
			return false;
		}
		if ($runtime > 5) {
			$sqldata = array(
				'type' => '2',
				'runtime' => $runtime,
				'runurl' => 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'],
				'runsql' => $sql,
				'createtime' => time()
			);
			logging_run(json_encode($sqldata),'trace', 'slow_sql_run');
		}
		return true;

	}

}
